#include "game.h"

#include <cstdlib>
//#include <QObject>
//#include <QTime>
#include <QKeyEvent>
#include <QMessageBox>
#include <QPushButton>

#include "openglpaintingarea.h"

void Game::setCanvas(Canvas* ca) {
    canvas = ca;
}

Canvas* Game::getCanvas() {
    return canvas;
}

void Game::setGameState(const Game::gt gs) {
    game_state = gs;
}

const Game::gt Game::getGameState() const {
    const auto tgt = game_state;
    return tgt;
}

void RussianBlock::Create(Canvas * ca) {
    setCanvas(ca);
    block_generator = new rb_types::RandomBlockGenerator();

    newGame(); /// be ready for a new game
}

void RussianBlock::setBlockGenerator(const RussianBlock::bgt gt) {
    if (getBlockGenerator() == gt) return ;
    if (block_generator != NULL) delete block_generator;
    switch (gt) {
    case bgt::random:
        block_generator = new rb_types::RandomBlockGenerator();
        break;
    case bgt::rolling:
        block_generator = new rb_types::RollingBlockGenerator();
        break;
    default:
        assert(!"unknown generator type");
        break;
    }
    //new_block = block_generator->fetch();
    //block_rotation = 0;
}

const RussianBlock::bgt RussianBlock::getBlockGenerator() const {
    const auto gt = block_generator->getGeneratorType();
    return gt;
}

void RussianBlock::newGame() {
    /// prepare new block
    speed_slot = fixed_speed_slot; // global variable
    new_block = block_generator->fetch();
    block_rotation = 0;
    /// prepare next block
    setNextBlock();
    /// prepare buffer
    std::vector<std::vector<bool> >().swap(buffer);
    for (int i = 0; i < board_height; ++i)
        buffer.push_back(getCanvas()->boardIndex[i]);
    for (int i = 0; i < board_height; ++i) {
        for (int j = 0; j < board_width; ++j) {
            buffer[i][j] = getCanvas()->boardIndex[i][j];
        }
    }
    vertical = 20;
    horizontal = 0;
}

void RussianBlock::afterGameOver() {
    qDebug() << "Game Over";
    setGameState(ctl_types::gaming_tag::game_over);
    ///todo : game over
}

void RussianBlock::keyPress(QKeyEvent *event) {
    //qDebug() << "Key Press in: " << event->key();
    /// control keys
    switch (event->key()) {
    case Qt::Key_Space: /// same with return key
    case Qt::Key_Enter: /// same with return key
    case Qt::Key_Return:
        if (getGameState() == ctl_types::gaming_tag::playing_game) {
            setGameState(ctl_types::gaming_tag::stopped_game);
            terminate();
        } else if (getGameState() == ctl_types::gaming_tag::stopped_game) {
            setGameState(ctl_types::gaming_tag::playing_game);
            start();
        }
        break;
    default:
        break;
    }
    /// paly-game key
    if (getGameState() != ctl_types::gaming_tag::playing_game) return ;
    assert(rb_types::block_shape_widths.count(new_block) && "out of range in map:block_shape_widths");
    std::vector<int> block_shape_width = rb_types::block_shape_widths.at(new_block);
    int old_horizontal_value = horizontal;
    int old_rotation_value = block_rotation;
    switch (event->key()) {
    // direction keys
    case Qt::Key_W:
        speed_slot *= 2;
        break;
    case Qt::Key_S:
        speed_slot /= 10;
        break;
    case Qt::Key_A:
        horizontal = horizontal - 1 > 0 ? horizontal - 1 : 0;
        if (checkPath()) horizontal = old_horizontal_value;
        updateCanvas();
        break;
    case Qt::Key_D:
        //assert(block_rotation == 0 && "shape changed");
        horizontal = horizontal + 1 < board_width - block_shape_width[block_rotation] ?
                    horizontal + 1 : board_width - block_shape_width[block_rotation];
        if (checkPath()) horizontal = old_horizontal_value;
        updateCanvas();
        break;
    // rotation keys
    case Qt::Key_J:
        block_rotation = block_rotation == 0 ? 3 : block_rotation - 1;
        if (checkPath()) block_rotation = old_rotation_value;
        updateCanvas();
        break;
    case Qt::Key_K:
        block_rotation = (block_rotation + 1) % 4;
        if (checkPath()) block_rotation = old_rotation_value;
        updateCanvas();
        break;
    default:
        break;
    }
}

void RussianBlock::keyRelease(QKeyEvent *event) {
    //qDebug() << "Key Release in: " << event->key();
    if (getGameState() != ctl_types::gaming_tag::playing_game) return ;
    switch (event->key()) {
    case Qt::Key_W:
        speed_slot /= 2;
        break;
    case Qt::Key_S:
        speed_slot *= 10;
        break;
    default:
        break;
    }
}

void RussianBlock::updateCanvas() {
    for (int i = 0; i < board_height; ++i) {
        for (int j = 0; j < board_width; ++j) {
            getCanvas()->boardIndex[i][j] = buffer[i][j];
        }
    }
    getCanvas()->boardIndex[block_shape[block_rotation][0].y+vertical][block_shape[block_rotation][0].x+horizontal] = true;
    getCanvas()->boardIndex[block_shape[block_rotation][1].y+vertical][block_shape[block_rotation][1].x+horizontal] = true;
    getCanvas()->boardIndex[block_shape[block_rotation][2].y+vertical][block_shape[block_rotation][2].x+horizontal] = true;
    getCanvas()->boardIndex[block_shape[block_rotation][3].y+vertical][block_shape[block_rotation][3].x+horizontal] = true;

    emit flush();
}

bool RussianBlock::checkPath() { ///检测路径是否非空
    bool check = false;
    for (int i = 0; i < 4; ++i) {
        assert(block_rotation >= 0 && block_rotation < 4);
        if (block_shape[block_rotation][i].x+horizontal < 0 ||
               block_shape[block_rotation][i].x+horizontal >= board_width) return true;
        if (block_shape[block_rotation][i].y+vertical < 0 ||
               block_shape[block_rotation][i].y+vertical >= board_height) return true;
        check |= buffer[block_shape[block_rotation][i].y+vertical][block_shape[block_rotation][i].x+horizontal];
    }
    return check;
}

void RussianBlock::setNextBlock() {
    rb_types::block_type next_block = block_generator->next();
    assert(rb_types::block_shapes.count(next_block) && "out of range in map:block_shapes");
    std::vector<std::vector<rb_types::Point> > next_block_shape = rb_types::block_shapes.at(next_block);
    for (int i = 0; i < 4; ++i) {
        getCanvas()->nextBlockCanvas[i].setVert(rb_types::Point(next_block_shape[0][i].x, next_block_shape[0][i].y+board_height));
    }
}

void RussianBlock::eliminate() {
    bool rowTest;
    int row = 0;
    for (int i = 0; i < board_height; ++i) {
        rowTest = true;
        for (int j = 0; j < board_width; ++j) {
            rowTest &= getCanvas()->boardIndex[i][j];
        }
        if (rowTest) {
            ///消除该行 && todo: 加分
            continue;
        }
        for (int j = 0; j < board_width; ++j) {
            buffer[row][j] = getCanvas()->boardIndex[i][j];
        }
        row++;
    }
    for (int i = row; i < board_height; ++i) {
        for (int j = 0; j < board_width; ++j) {
            buffer[i][j] = false;
        }
    }
    for (int i = 0; i < board_height; ++i) {
        for (int j = 0; j < board_width; ++j) {
            getCanvas()->boardIndex[i][j] = buffer[i][j];
        }
    }
    msleep(100);
    emit flush();
}

RussianBlock::~RussianBlock() {
    assert(block_generator != NULL);
    delete block_generator;
}

void RussianBlock::run() {
    //qDebug() << "game start";
    assert(rb_types::block_shapes.count(new_block) && "out of range in map:block_shapes");
    block_shape = rb_types::block_shapes.at(new_block);

    while (true) {
        if (vertical < 0) { // 生成的块最多能下落20步 (或 改成检测块的4个坐标是否合法(不越界))
            ///检测消除行
            eliminate();
            ///归零
            vertical = 20;
            horizontal = 0;
            block_rotation = 0;
            ///产生新块
            new_block =  block_generator->fetch();
            block_shape = rb_types::block_shapes.at(new_block);
            setNextBlock();
            //getCanvas()->boardIndex.swap(buffer);
            for (int i = 0; i < board_height; ++i) {
                for (int j = 0; j < board_width; ++j) {
                    buffer[i][j] = getCanvas()->boardIndex[i][j];
                }
            }
        }
        /// 检测路径是否非空
        bool check = checkPath();

        if (check && vertical == 20) { /// 检查是否封顶
            afterGameOver();
            break; ///terminate
        }
        if (check) { /// 路径非空，
            ///检测消除行
            eliminate();
            ///归零
            vertical = 20;
            horizontal = 0;
            block_rotation = 0;
            ///产生新块
            new_block =  block_generator->fetch();
            block_shape = rb_types::block_shapes.at(new_block);
            setNextBlock();
            for (int i = 0; i < board_height; ++i) {
                for (int j = 0; j < board_width; ++j) {
                    buffer[i][j] = getCanvas()->boardIndex[i][j];
                }
            }
            continue; //跳过减1，使封顶检测有效
        }

        updateCanvas();
        msleep(speed_slot);

        vertical--;
    }

    //QTime run_time;
    //std::chrono::system_clock::now().time_since_epoch().count();
}
